package scriptEngine

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisService"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/SyncMap"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeEntity"
	"github.com/dop251/goja"
	"sync"
)

type scriptEngineService struct {
	engineMap sync.Map
}

func (e *scriptEngineService) getScriptEngine(operateEntityKey string) (*Decoder, error) {
	decoder, err := SyncMap.GetValue(&e.engineMap, operateEntityKey)
	if err != nil {
		return nil, err
	}
	if decoder == nil {
		return nil, errors.New("找不到对应的引擎")
	}

	return decoder.(*Decoder), nil
}

func (e *scriptEngineService) rebindScriptEngine(operateEntity *edgeEntity.OperateEntity) (err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			message := fmt.Sprintf("rebindScriptEngine()捕获到异常:%v", r)
			commLogger.Console.Error(message)
			err = errors.New(message)
		}
	}()

	// 检查：operateEntity的类型是否符合要求
	if operateEntity.OperateMode != "keyHandler" || operateEntity.ServiceType != "channel" || operateEntity.EngineType != "JavaScript" {
		return errors.New("其他不支持的操作")
	}

	// 构造引擎
	return service.buildEngine(operateEntity)
}

func (e *scriptEngineService) buildScriptEngine(scripts []interface{}) []string {
	list := make([]string, 0)

	for _, v := range scripts {
		dat := v.(map[string]interface{})

		manufacturer := Map.GetString(dat, "manufacturer", "")
		deviceType := Map.GetString(dat, "deviceType", "")
		operateName := Map.GetString(dat, "operateName", "")

		operateEntity, err := service.getKeyOperate(manufacturer, deviceType, operateName)
		if err != nil || operateEntity == nil {
			continue
		}

		// 检查：operateEntity的类型是否符合要求
		if operateEntity.OperateMode != "keyHandler" || operateEntity.ServiceType != "channel" || operateEntity.EngineType != "JavaScript" {
			continue
		}

		// 构造引擎
		err = service.buildEngine(operateEntity)
		if err != nil {
			continue
		}

		list = append(list, operateEntity.MakeServiceKey())
	}

	return list
}

func (e *scriptEngineService) buildEngine(operateEntity *edgeEntity.OperateEntity) (err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	// 检测：收发为JSP引擎
	if operateEntity.EngineType != "JavaScript" {
		return errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.OperateMode != "keyHandler" {
		return errors.New("获得操作实体，不是一个Jsp引擎方法")
	}
	if operateEntity.ServiceType != "channel" {
		return errors.New("获得操作实体，不是一个Jsp引擎方法")
	}

	// 检测：引擎参数是否正确
	decode := operateEntity.EngineParam["decode"]
	if Method.HasEmpty(decode) {
		return errors.New("engineParam为空")
	}

	// 检测：脚本是否正确
	decodeMap := decode.(map[string]interface{})
	decodeScript := decodeMap["code"]
	main := decodeMap["main"]
	if Method.HasEmpty(main, decodeScript) {
		return errors.New("未定义解码脚本")
	}

	// 获得引擎
	scriptEngine := goja.New()

	// 装载脚本
	scriptEngine.RunString(decodeScript.(string))

	// 执行JSP脚本中的函数
	_, ok := goja.AssertFunction(scriptEngine.Get("getServiceKey"))
	if !ok {
		return errors.New("getServiceKey()函数")
	}

	decoder := &Decoder{}
	decoder.engine = scriptEngine
	decoder.script = decodeScript.(string)
	decoder.Manufacturer = operateEntity.Manufacturer
	decoder.DeviceType = operateEntity.DeviceType
	decoder.OperateName = operateEntity.OperateName

	SyncMap.SetValue(&e.engineMap, operateEntity.MakeServiceKey(), decoder)
	return nil
}

func (e *scriptEngineService) getKeyOperate(manufacturer, deviceType, operateName string) (entity *edgeEntity.OperateEntity, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			entity = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	if Method.HasEmpty(manufacturer, deviceType, operateName) {
		return nil, errors.New("参数不能为空: manufacturer, deviceType, operateName")
	}

	operateEntity := edgeEntity.Type.OperateEntity.NewEntity().(*edgeEntity.OperateEntity)
	operateEntity.Manufacturer = manufacturer
	operateEntity.DeviceType = deviceType
	operateEntity.OperateName = operateName

	exist, err := commRedisService.GetEntityByServiceKey(operateEntity.Type(), operateEntity.MakeServiceKey())
	if err != nil || exist == nil {
		return nil, errors.New("参数不能为空: 找不到对应的操作实体")
	}

	return exist.(*edgeEntity.OperateEntity), nil
}
